﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace CodeAngel.Domain.Framework
{
    #region IRule

    /// <summary>
    /// Interface for an object that defines a business rule.
    /// </summary>
    public interface IRule
    {
        /// <summary>
        /// Identifies the name of this rule suitable for displaying to an end user.
        /// </summary>
        string Name { get; }

        /// <summary>
        /// Identifies a description of this rule suitable for displaying to an end user.
        /// </summary>
        /// <Remarks>Do not use this to describe a rule violation - use the ViolationDescription property.</Remarks>
        string Description { get; }

        /// <summary>
        /// Determines if this rule is currently valid.
        /// </summary>
        bool IsValid();

        /// <summary>
        /// Describes the rule violation, with appropriate state information (such as the current property value) 
        /// if this rule is currently invalid.
        /// </summary>
        string ViolationDescription { get; }
    }

    #endregion

    #region RuleCollection

    /// <summary>
    /// A container to hold a set of IRule objects.
    /// </summary>
    public class RuleCollection : IEnumerable<IRule>
    {
        /// <summary>
        /// The rules in this collection.
        /// </summary>
        private List<IRule> _Rules = new List<IRule>();

        #region Add
        
        /// <summary>
        /// Adds the identified rule to this collection.
        /// </summary>
        public void Add(IRule rule)
        {
            this._Rules.Add(rule);
        }

        /// <summary>
        /// Adds the rules from the identified collection to this collection.
        /// </summary>
        /// <param name="rules"></param>
        public void AddRange(IEnumerable<IRule> rules)
        {
            foreach (var ThisRule in rules)
                this.Add(ThisRule);
        }

        #endregion

        #region IsValid

        /// <summary>
        /// Determines if all the rules in this collection are currently valid.
        /// </summary>
        public bool IsValid()
        {
            foreach (var ThisRule in this._Rules)
                if (!ThisRule.IsValid()) return false;
            return true;
        }



        #endregion

        #region GetInvalidRules
        
        /// <summary>
        /// Identifies the subset of rules within this collection that are currently invalid, if any.
        /// </summary>
        public RuleCollection GetInvalidRules()
        {
            var InvalidRules = new RuleCollection();
            foreach (IRule ThisRule in this._Rules)
                if (!ThisRule.IsValid()) InvalidRules.Add(ThisRule);
            
            return InvalidRules;
        }

        #endregion

        #region ToViolationString

        public string ToViolationString()
        {
            string ThisString = string.Empty;
            bool InvalidRuleAdded = false;
            foreach (var ThisRule in this._Rules)
            {
                if (!ThisRule.IsValid())
                {
                    if (InvalidRuleAdded) ThisString += Environment.NewLine;
                    ThisString += ThisRule.ViolationDescription;
                    InvalidRuleAdded = true;
                }
            }
            return ThisString;
        }

        #endregion


        public string GetAllViolationDescriptions()
        {
            string ThisString = string.Empty;
            bool InvalidRuleAdded = false;
            foreach (var ThisRule in this._Rules)
            {
                if (InvalidRuleAdded) ThisString += Environment.NewLine;
                ThisString += ThisRule.ViolationDescription;
                InvalidRuleAdded = true;
            }
            return ThisString;        
        }

        #region IEnumerable Members

        public IEnumerator<IRule> GetEnumerator()
        {
            return this._Rules.GetEnumerator();
        }

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return this._Rules.GetEnumerator();
        }

        #endregion
    }

    #endregion

    #region EntityRule

    /// <summary>
    /// Base class for a business rule that applies to a specific entity.
    /// </summary>
    public abstract class EntityRule : IRule
    {
        #region Constructors
        
        public EntityRule(Entity entity)
        {
            this.Entity = entity;
        }

        #endregion

        /// <summary>
        /// Identifies the entity this rule applies to.
        /// </summary>
        public Entity Entity { get; private set; }

        /// <summary>
        /// Identifies the name of this rule suitable for displaying to an end user.
        /// </summary>
        public abstract string Name { get; }

        /// <summary>
        /// Identifies a description of this rule suitable for displaying to an end user.
        /// </summary>
        /// <Remarks>Do not use this to describe a rule violation - use the ViolationDescription property.</Remarks>
        public abstract string Description { get; }

        /// <summary>
        /// Describes the rule violation, if this rule is currently invalid.
        /// </summary>
        public abstract string ViolationDescription { get; }

        /// <summary>
        /// Determines if this rule is currently valid.
        /// </summary>
        public abstract bool IsValid();
        
    }

    #endregion

    #region ValuePropertyRule

    /// <summary>
    /// Base class for a business rule that applies to a value property of an entity.
    /// </summary>
    public abstract class ValuePropertyRule : EntityRule
    {
        public ValuePropertyRule(IValueProperty valueProperty)
            : base(valueProperty.ParentEntity)
        {
            this.ValueProperty = valueProperty;
        }

        /// <summary>
        /// Identifies the value property this rule applies to.
        /// </summary>
        public IValueProperty ValueProperty { get; private set; }

        /// <summary>
        /// Determines if the identified value is valid for this rule.
        /// </summary>
        public abstract bool IsValueValid(object value);

    }

    #endregion

    #region EntityCollectionPropertyRule

    ///// <summary>
    ///// Base class for a business rule that applies to an entity collection property of an entity.
    ///// </summary>
    //public abstract class EntityCollectionPropertyRule : EntityRule
    //{

    //    public EntityCollectionPropertyRule(IEntityCollectionProperty entityCollectionProperty, Entity entity)
    //        : base(entity)
    //    {
    //        this.EntityCollectionProperty = entityCollectionProperty;
    //        this.EntityCollectionProperty.EntityAdded += delegate { this.UpdateIsValid(); };
    //        this.EntityCollectionProperty.EntityRemoved += delegate { this.UpdateIsValid(); };
    //    }
        
    //    /// <summary>
    //    /// Identifies the collection property this rule applies to.
    //    /// </summary>
    //    public IEntityCollectionProperty EntityCollectionProperty { get; private set; }

    //    /// <summary>
    //    /// Asks the derived class to update the IsValid state of this rule.
    //    /// </summary>
    //    protected abstract bool UpdateIsValid();

    //}

    #endregion

    #region DeleteEntityRule

    /// <summary>
    /// Base class for a business rule that defines whether or not an entity can be deleted.
    /// </summary>
    /// <Remarks>If this rule is not valid, then trying to delete the respective entity should fail.</Remarks>
    public abstract class DeleteEntityRule : EntityRule
    {
        public DeleteEntityRule(Entity entity) : base(entity) { }
    }

    #endregion

    //#region RuleException

    ///// <summary>
    ///// An exception that is thrown when a rule is asked to validate itself and that rule is not valid.
    ///// </summary>
    //public class RuleException : Exception
    //{

    //    public RuleException(IRule rule)
    //    {
    //        this.Rule = rule;
    //    }

    //    /// <summary>
    //    /// Identifies the rule that has been violated.
    //    /// </summary>
    //    public IRule Rule { get; private set; }

    //    /// <summary>
    //    /// Identifies the exception message.
    //    /// </summary>
    //    public override string Message
    //    {
    //        get { return this.Rule.ViolationDescription; }
    //    }

    //    public override string ToString()
    //    {
    //        return this.Message;
    //    }

    //}

    //#endregion
}
